/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package m12cqis01.nhathv.servlet;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jpl.Atom;
import jpl.JPL;
import jpl.Query;
import jpl.Term;
import m12cqis01.nhathv.parser.GGResponeParser;
import m12cqis01.nhathv.service.Service;
import m12cqis01.nhathv.util.BoDauTiengViet;
import m12cqis01.nhathv.util.Constants;
import m12cqis01.nhathv.util.RegexUtil;
import m12cqis01.nhathv.vo.GGItemVO;
import m12cqis01.nhathv.vo.QueryVO;
import m12cqis01.nhathv.vo.ResultVO;
import m12cqis01.nhathv.util.Constants.REQUIREMENT_TYPE;
import m12cqis01.nhathv.util.DateTimeMaster;
import m12cqis01.nhathv.vo.ProductVO;

/**
 *
 * @author NhatHV
 */
public class SearchServlet extends HttpServlet {

    static ArrayList<String> PROLOG_FILE;
    static ArrayList<String> GSE_KEYS;
    static int requestCount = 0;

    static {
        // initial prolog files
        PROLOG_FILE = initialPrologFiles();
        // initial google search engine key
        GSE_KEYS = initialGSEKey();
        // initial Prolog machine
        JPL.init();
        // allow load prolog file from internet
        Query query1
                = new Query("[library(http/http_load)].");
        query1.query();
    }

    private static ArrayList<String> initialPrologFiles() {
        ArrayList<String> prologs = new ArrayList<String>();
        prologs.add("http://localhost:8080/HeThongTruyVanDienLanh/prolog/sentence001.pl");
        prologs.add("http://localhost:8080/HeThongTruyVanDienLanh/prolog/sentence001_01.pl");
        prologs.add("http://localhost:8080/HeThongTruyVanDienLanh/prolog/sentence002.pl");
        prologs.add("http://localhost:8080/HeThongTruyVanDienLanh/prolog/sentence002_01.pl");
        prologs.add("http://localhost:8080/HeThongTruyVanDienLanh/prolog/sentence002_02.pl");
        prologs.add("http://localhost:8080/HeThongTruyVanDienLanh/prolog/sentence002_04.pl");
        prologs.add("http://localhost:8080/HeThongTruyVanDienLanh/prolog/sentence003.pl");
        prologs.add("http://localhost:8080/HeThongTruyVanDienLanh/prolog/sentence006.pl");

        return prologs;
    }

    private static ArrayList<String> initialGSEKey() {
        ArrayList<String> gseKeys = new ArrayList<String>();
        gseKeys.add(Constants.GSE_KEY_01);
        gseKeys.add(Constants.GSE_KEY_02);

        return gseKeys;
    }

    private static String getGSEKey(ArrayList<String> gseKeys) {
        Random random = new Random();
        int number = random.nextInt(gseKeys.size());

        return gseKeys.get(number);
    }

    /**
     * Processes requests for both HTTP <code>GET</code> and <code>POST</code>
     * methods.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        DateTimeMaster timerStart;
        DateTimeMaster timerEnd;
        float timeStart;
        float timeEnd;

        // get query from user
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");

        String query = (String) request.getParameter("query").replaceAll(RegexUtil.USELESS_CHAR, "").toLowerCase();

        // lay thoi gian luc bat dau
        timerStart = new DateTimeMaster();
        timeStart = Float.parseFloat(timerStart.calculateSecond(timerStart.getMinute(),
                timerStart.getSecond(), timerStart.getMilisecond()));

        // get result from internet
        ResultVO resultVO = executeQuestion(query);

        // lay thoi gian sau khi truy van
        timerEnd = new DateTimeMaster();
        timeEnd = Float.parseFloat(timerEnd.calculateSecond(timerEnd.getMinute(),
                timerEnd.getSecond(), timerEnd.getMilisecond()));

        // show result page
        request.setAttribute("query", query);
        request.setAttribute("resultVO", resultVO);
        request.setAttribute("time", timeEnd - timeStart);

        RequestDispatcher dispatcher = request.getRequestDispatcher("/result.jsp");
        dispatcher.forward(request, response);
    }

    /**
     * Load the DCG File to Prolog.
     *
     * @param filePath Real path of DCG file.
     * @return true: load successfully, false: load fail.
     */
    private boolean loadDCGFile(String filePath) {
        Term consult_arg[] = {
            new Atom(filePath)
        };
        Query consult_query = new Query("consult", consult_arg);

        return consult_query.query();
    }

    /**
     * Pre-Process the query of user. Convert Natural language to Prolog
     * language.
     *
     * @param strTruyVan The query of user.
     * @return The query of Prolog. sentence(Parse, [word1, word2, word3],[]).
     */
    private static String preProcessProlog(String strQuery) {
        String strPreProcess = "sentence(Parse,[";
        // Convert Vietnamese character to UnSign Vietnamese charater
        String strRemoveVN = BoDauTiengViet.toUnicodeVNI(strQuery.trim().toLowerCase());
        String strTemp[] = strRemoveVN.split(" ");
        for (String strTemp1 : strTemp) {
            strPreProcess += strTemp1 + ",";
        }
        // Remove the last character, that is ","
        strPreProcess = strPreProcess.substring(0, strPreProcess.length() - 1);
        // Add " ],[]). " to the end of string
        strPreProcess += "],[]).";

        return strPreProcess;
    }

    /**
     * Process Prolog with query (converted to prolog language) and imported DCG
     *
     * @param prologQuery Prolog language.
     * @return Result of process.
     */
    private String processProlog(String prologQuery) {
        Query query = new Query(prologQuery);
//        String result = query.oneSolution().toString();

        String result = "false";
        if (query.hasSolution()) {
            result = query.oneSolution().toString();
        }
        return result;
    }

    /**
     * Post-process Prolog. Convert prolog result (String) to Query object.
     *
     * @param prologResult prolog result string. ex: "Parse =
     * s(np(n_tuhoi('ki1ch thu7o71c'), e(cu3a), sp(n_sp('tu3 la5nh'),
     * n(n_hsx(sharp), pron_sp([SJ16VSL])))), vp(r(la2, 'bao nhie6u'))) ."
     * @return QueryVO object.
     */
    private QueryVO postProcessProlog(String prologResult) {
        // Get Requirement
        String requirementTmp = RegexUtil.findMatch(prologResult, RegexUtil.REGEX_N_TUHOI);
        String producerTmp = RegexUtil.findMatch(prologResult, RegexUtil.REGEX_N_HSX);
        String productNameTmp = RegexUtil.findMatch(prologResult, RegexUtil.REGEX_N_SP);
        String productCodeTmp = RegexUtil.findMatch(prologResult, RegexUtil.REGEX_PRON_SP);
        String reqPropertyTmp = RegexUtil.findMatch(prologResult, RegexUtil.REGEX_R_PHUTU);

        String requirement = requirementTmp.replace(Constants.N_TUHOI, "")
                .replace("(", "")
                .replace(")", "")
                .replace("'", "");

        String producer = producerTmp.replace(Constants.N_HANGSANXUAT, "")
                .replace("(", "")
                .replace(")", "");
        String productName = productNameTmp.replace(Constants.N_SANPHAM, "")
                .replace("(", "")
                .replace(")", "")
                .replace("'", "");
        String productCode = productCodeTmp.replace(Constants.PRON_SP, "")
                .replace("(", "")
                .replace(")", "")
                .replace("[", "")
                .replace("]", "");
        String reqProperty = reqPropertyTmp.replace(Constants.R_PHUTU, "")
                .replace("(", "")
                .replace(")", "")
                .replace("'", "")
                .replace(",", "")
                .replace(Constants.W_LA2, "");

        QueryVO queryVO = new QueryVO();
        queryVO.setRequirement(requirement);
        queryVO.setProducer(producer);
        queryVO.setProductName(productName);
        queryVO.setProductCode(productCode);
        queryVO.setProperty(reqProperty);

        return queryVO;
    }

    /**
     * Execute question. load the specified prolog file. search with Google, and
     * get data.
     *
     * @param question
     * @return
     */
    private ResultVO executeQuestion(String question) {
        ResultVO result = new ResultVO();

        // pre-process prolog
        String preProcessProlog = preProcessProlog(question);

        String processProlog = "false";
        for (String string : PROLOG_FILE) {
            // load file prolog
            loadDCGFile(string);

            // process prolog
            processProlog = processProlog(preProcessProlog);
            System.out.println("executeQuestion, Prolog: " + processProlog);
            if (!processProlog.equalsIgnoreCase("false")) {
                break;
            }
        }

        if (processProlog.equalsIgnoreCase("false")) {
            // show No result page
            return result;
        }

        // post-process prolog
        QueryVO queryVO = postProcessProlog(processProlog);

        ResultVO tmpResult = new ResultVO();
        int page = 1;
        do {
            // get data from google
            ArrayList<GGItemVO> ggItemVOList = searchWithGoogle(queryVO, page);
            if (ggItemVOList == null) {
                break;
            } else if (ggItemVOList.size() < 0) {
                break;
            }

            // get answer from google
            tmpResult = getAnswer(ggItemVOList, queryVO);

            // get other page if no result
            if (!tmpResult.getResult().equalsIgnoreCase("")) {
                break;
            } else {
                page++;
            }
        } while (page <= Constants.MAX_PAGE);

        // Result
        if (!tmpResult.getResult().equalsIgnoreCase("")) {
            result.setQueryVO(queryVO);
            result.setResult(tmpResult.getResult());
            result.setUrl(tmpResult.getUrl());
        }

        return result;
    }

    /**
     * Search with Google.
     *
     * @param queryVO
     * @param page
     * @return
     */
    private ArrayList<GGItemVO> searchWithGoogle(QueryVO queryVO, int page) {
        ArrayList<GGItemVO> resultList;

        String query = "";
        if (queryVO.getRequirement().contains(Constants.TH_THONGSO)) {
            query += "";
        } else {
            query += BoDauTiengViet.toVietNamNatural(queryVO.getRequirement());
        }

        query += " " + BoDauTiengViet.toVietNamNatural(queryVO.getProductName());
        query += " " + queryVO.getProducer();
        query += " " + queryVO.getProductCode();
        String url = "";
        try {
            url = "https://www.googleapis.com/customsearch/v1?"
                    + "key=" + getGSEKey(GSE_KEYS)
                    + "&cx=013036536707430787589:_pqjad5hr1a"
                    + "&num=10"
                    + "&alt=json"
                    + "&start=" + (1 + ((page - 1) * 10))
                    + "&q=" + URLEncoder.encode(query, "UTF-8");
            if (queryVO.getRequirement().contains(Constants.TH_THONGSO)) {
                url += "&siteSearch=http://mediamart.vn/";
            }
        } catch (UnsupportedEncodingException ex) {
            Logger.getLogger(SearchServlet.class.getName()).log(Level.SEVERE, null, ex);
        }
        System.out.println("searchWithGoogle, url = " + url);
        Service service = new Service();
        String result = service.getURL(url);

        GGResponeParser ggResponseParser = new GGResponeParser();
        resultList = ggResponseParser.parseData(result);

        return resultList;
    }

    /**
     * Get Measure
     *
     * @param items
     * @return measure
     */
    private ResultVO getAnswer(ArrayList<GGItemVO> items, QueryVO queryVO) {

        String requirement = queryVO.getRequirement();
        REQUIREMENT_TYPE requirementType;
        if (requirement.equalsIgnoreCase(Constants.KICH_THUOC)) {
            requirementType = REQUIREMENT_TYPE.MEASURE;

        } else if (requirement.equalsIgnoreCase(Constants.GIA_BAN)) {
            requirementType = REQUIREMENT_TYPE.PRICE;

        } else if (requirement.equalsIgnoreCase(Constants.CONG_SUAT)) {
            requirementType = REQUIREMENT_TYPE.CAPACITY;

        } else if (requirement.equalsIgnoreCase(Constants.TH_MAU)) {
            requirementType = REQUIREMENT_TYPE.COLOR;

        } else if (requirement.contains(Constants.TH_THONGSO)) {
            requirementType = REQUIREMENT_TYPE.SPECS;

        } else {
            requirementType = REQUIREMENT_TYPE.UNDEFINED;
        }

        ResultVO resultVO = new ResultVO();

        String answer = "";
        for (GGItemVO gGItemVO : items) {
            String title = gGItemVO.getTitle();
            if (title.contains(queryVO.getProductCode())) {

                String snippet = gGItemVO.getSnippet();
                System.out.println("getAnswer, snippet = " + snippet);
                switch (requirementType) {
                    case MEASURE: {
                        answer = RegexUtil.findMatch(snippet, RegexUtil.REGEX_ANSWER_MEASURE);
                        break;
                    }
                    case PRICE: {
                        answer = RegexUtil.findMatch(snippet, RegexUtil.REGEX_ANSWER_PRICE);
                        if (answer.length() != 0 || !answer.equalsIgnoreCase("")) {
                            ArrayList<ProductVO> products = gGItemVO.getProducts();
                            ArrayList<String> offers = gGItemVO.getOffers();
                            if (products.size() > 0 && offers.size() > 0) {
                                for (int i = 0; i < products.size(); i++) {
                                    if (products.get(i).getName().contains(queryVO.getProductCode())) {
                                        answer = offers.get(i);
                                        break;
                                    }
                                }
                            }
                        }

                        break;
                    }
                    case CAPACITY: {
                        if (queryVO.getProductName().equalsIgnoreCase(Constants.P_TULANH)) {                        
                            answer = RegexUtil.findMatch(snippet, RegexUtil.REGEX_ANSWER_CAPACITY_TULANH);
                        }
                        break;
                    }
                    case COLOR: {
                        answer = RegexUtil.findMatch(snippet, RegexUtil.REGEX_ANSWER_COLOR);

                        break;
                    }
                    case SPECS: {
                        ArrayList<ProductVO> products = gGItemVO.getProducts();
                        if (products.size() > 0) {
                            answer = products.get(0).getDescription();
                        }
                        break;
                    }
                    default:
                        break;
                }
            }
            if (answer.length() != 0 || !answer.equalsIgnoreCase("")) {
                resultVO.setResult(answer);
                resultVO.setUrl(gGItemVO.getLink());
                break;
            }
        }

        return resultVO;
    }

    // <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
    /**
     * Handles the HTTP <code>GET</code> method.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /**
     * Handles the HTTP <code>POST</code> method.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /**
     * Returns a short description of the servlet.
     *
     * @return a String containing servlet description
     */
    @Override
    public String getServletInfo() {
        return "Short description";
    }// </editor-fold>

}
